package br.mia.unifor.olio.artifact;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import br.mia.unifor.onaga.cloud.CloudTestCase;
import br.mia.unifor.onaga.cloud.artifact.Instance;
import br.mia.unifor.onaga.cloud.artifact.Metric;
import br.mia.unifor.onaga.cloud.artifact.MetricEval;
import br.mia.unifor.onaga.cloud.artifact.Scenario;
import br.unifor.mia.olio.xml.BenchResult;
import br.unifor.mia.olio.xml.ExecutionList;
import br.unifor.mia.olio.xml.ExecutionModel;
import br.unifor.mia.olio.xml.Operation;

import com.thoughtworks.selenium.*;

import org.apache.bcel.Repository;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;

public class OlioTest extends CloudTestCase {

	protected DefaultSelenium selenium;
	private Logger logger = Logger.getLogger(this.getClass().getName());
	private String currentExecution = null;

	public OlioTest(Scenario scenario) {
		super(scenario);
	}

	private void runStartUpScript(String ip, String script) throws IOException {

		String command = "plink -i G:\\OLIO-AWS-US-East.ppk ubuntu@" + ip + " "
				+ script;
		logger.info(command);
		Process process = Runtime.getRuntime().exec(command);

	}

	@Override
	public void testRun(String user) throws Exception {
		OlioAppliance appliance = (OlioAppliance) scenario.getAppliance();

		selenium = new DefaultSelenium(
				"localhost",
				4444,
				"*firefox",
				"http://" + appliance.getDriver().getPublicIpAddress()
						+ ":9980/");
		selenium.start();

		logger.info("aí!!");

		selenium.open("http://" + appliance.getDriver().getPublicIpAddress()
				+ ":9980/");
		selenium.selectFrame("menu");
		selenium.click("link=Schedule Run");
		
		selenium.waitForPageToLoad("30000");
		
		selenium.selectFrame("relative=up");
		selenium.selectFrame("main");

		//selenium.waitForPageToLoad("30000");

		if (selenium.isElementPresent("//input[@value='Select']"))
			selenium.click("//input[@value='Select']");

		while (!selenium.isElementPresent("t_trigger-runConfig")) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				logger.log(Level.SEVERE, "não foi possível parar", e);
			}
		}

		selenium.click("t_trigger-runConfig");
		selenium.waitForPageToLoad("30000");
		selenium.type("input-description-value", getDescription());
		selenium.type("input-agent-host-value", appliance.getDriver()
				.getPrivateIpAddress());
		selenium.type("input-scale-value", user);
		selenium.click("t_trigger-appserver");
		selenium.waitForPageToLoad("30000");
		selenium.type("input-appserver-hostPorts-value",
				getEndPointString(appliance.getWeb(), "3000"));

		String thinServer = null;
		for (Object property : appliance.getWeb().get(0).getType()
				.getProperties()) {
			HashMap<String, String> map = (HashMap<String, String>) property;
			thinServer = map.get("thinServers");
			if (thinServer != null) {
				break;
			}
		}

		selenium.type("input-appserver-numInstances-value", thinServer);
		selenium.type("input-proxyserver-hostPorts-value",
				getEndPointString(appliance.getWeb(), "80"));
		selenium.click("t_trigger-servers");
		selenium.waitForPageToLoad("30000");
		selenium.type("input-dbServer-host-value", appliance.getDatabase()
				.getPrivateIpAddress());
		selenium.type(
				"input-connectURL-value",
				"jdbc:mysql://"
						+ appliance.getDatabase().getPrivateIpAddress()
						+ ":3306/olio?user=olio&password=olio&relaxAutoCommit=true&sessionVariables=FOREIGN_KEY_CHECKS=0");
		selenium.type("input-load-scale-value", user.toString());
		selenium.type("input-dataStorage-host-value", appliance.getWeb().get(0)
				.getPrivateIpAddress());
		selenium.click("t_trigger-ok");
		selenium.waitForPageToLoad("30000");

		selenium.stop();

		while (!TestUrl.testUrl("http://"
				+ appliance.getDriver().getPublicIpAddress()
				+ ":9980/pending-runs.jsp", "There are no pending runs")) {
			Thread.sleep(300000);
		}

		/*while (TestUrl.testUrl("http://"
				+ appliance.getDriver().getPublicIpAddress()
				+ ":9980/controller/results/list", "running.png")) {
			Thread.sleep(150000);
		}*/
		
		while (isRunning()){
			Thread.sleep(1000*30);
		}
			
	}

	public String getDescription() {
		String retorno = "";
		for (Instance instance : scenario.getAppliance().getInstances()) {
			retorno += instance.getName()+" ("+ instance.getType().getProviderProfile()+ ") | ";
		}
		return retorno;
	}

	public String getEndPointString(List<Instance> endPoints, String port) {
		String retorno = "";
		for (Instance endString : endPoints) {
			retorno += (endString.getPrivateIpAddress() + ":" + port + " ");
		}
		return retorno;
	}

	private static class TestUrl {
		private static Logger logger = Logger.getLogger(TestUrl.class
				.getClass().getName());

		public static boolean testUrl(String url, String pattern)
				throws ClientProtocolException, IOException {

			HttpClient httpclient = new DefaultHttpClient();
			HttpGet httpget = new HttpGet(url);

			logger.info("executing request " + httpget.getURI());
			// Create a response handler
			ResponseHandler<String> responseHandler = new BasicResponseHandler();
			String responseBody = httpclient.execute(httpget, responseHandler);

			return responseBody.contains(pattern);
		}

	}

	@Override
	public List<MetricEval> getResult() {
		BenchResult result = getBenchResult(currentExecution,
				((OlioAppliance) scenario.getAppliance()).getDriver()
						.getPublicIpAddress());

		Boolean passed = true;

		for (Operation operation : result.getDriverSummary().getOperations()) {
			passed &= operation.isPassed();
		}

		if (passed) {
			for (Operation operation : result.getDriverSummary()
					.getResponseTimes()) {
				passed &= operation.isPassed();
			}

			if (passed) {
				for (Operation operation : result.getDriverSummary()
						.getDelayTimes()) {
					passed &= operation.isPassed();
				}

				if (passed) {
					int i = 0;
					for (Operation operation : result.getDriverSummary()
							.getDelayTimes()) {

						if (operation.isPassed()) {
							i++;
						}
						passed = i >= 3;

					}

					if (!passed) {
						logger.severe(currentExecution + " VERIFICAR ");
					}
				} else {
					logger.info(currentExecution
							+ " perdeu na TERCEIRA parte (tempo)");
				}

			} else {
				logger.info(currentExecution
						+ " perdeu na SEGUNDA parte (tempo)");
			}

		} else {
			logger.info(currentExecution + " perdeu na PRIMEIRA parte");
		}
		
		List<MetricEval> retorno = new ArrayList<MetricEval>(scenario.getMetrics().size());
		for (Metric metric : scenario.getMetrics()) {
			MetricEval eval = new MetricEval(metric);
			retorno.add(eval);
			eval.setDescription(getDescription());
			eval.setId(currentExecution);
			eval.setPassed(passed);
		}

		return retorno;
	}

	@Override
	public boolean isRunning() {
		String tmp;
		try {
			tmp = getCurrentExecution(((OlioAppliance) scenario.getAppliance())
					.getDriver().getPublicIpAddress());

			if (tmp == null) {
				return false;
			} else {
				currentExecution = tmp;
				return true;
			}

		} catch (IOException e) {
			logger.log(Level.SEVERE, "erro ao verficar se a app está rodando",
					e);
			return false;
		}

	}
	
	public BenchResult getBenchResult(String id, String ip) {
		BenchResult result = null;
		try {
			JAXBContext jc = JAXBContext.newInstance(BenchResult.class);
			Unmarshaller um = jc.createUnmarshaller();
			URL url = new URL("http://" + ip + ":9980/output/" + id
					+ "/summary.xml");
			result = (BenchResult) um.unmarshal(url);
		} catch (JAXBException e) {
			logger.log(Level.SEVERE, "erro nao identificado", e);
		} catch (MalformedURLException e) {
			logger.log(Level.SEVERE, "faban server not available", e);
		}

		return result;
	}
	
	private String getCurrentExecution(String ip) throws IOException {
		String content = ExecutionList.loadUrl("http://" + ip
				+ ":9980/controller/results/list");

		ExecutionList list = new ExecutionList(content);

		list.run();

		for (ExecutionModel model : list.getExecutions()) {

			if (model.getResult().contains("running.png")) {
				return model.getName();
			}
		}

		return null;
	}

}
